<!-- DraggableBox.vue -->
<template>
  <!-- 父容器（限制范围） -->
  <div ref="parent" class="page">
    <!-- 可拖拽元素 -->
    <div class="box" :style="style" @mousedown.stop="onMouseDown">
      <div style="height: 50px; width: 50px; background-color: red"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

/* ------------ 位置与状态 ------------- */
const x = ref(100)
const y = ref(100)
const dragging = ref(false)

/* ------------ 获取父容器尺寸 ------------- */
const parent = ref<HTMLDivElement>() // template 中同名 ref
const parentRect = ref<DOMRect>() // 缓存父容器矩形

/* ------------ 计算样式 ------------- */
const style = computed(() => ({
  left: `${x.value}px`,
  top: `${y.value}px`,
  cursor: dragging.value ? 'grabbing' : 'grab'
}))

/* ------------ 拖拽核心 ------------- */
let offsetX = 0
let offsetY = 0

function onMouseMove(e: MouseEvent) {
  if (!dragging.value) return

  const pr = parentRect.value!
  const boxWidth = 120 // 与 css 保持一致
  const boxHeight = 120

  // 计算未限制的新位置
  let newX = e.clientX - offsetX
  let newY = e.clientY - offsetY

  // 限制在父容器内
  newX = Math.max(0, Math.min(newX, pr.width - boxWidth))
  newY = Math.max(0, Math.min(newY, pr.height - boxHeight))

  x.value = newX
  y.value = newY
}

function onMouseUp() {
  dragging.value = false
  document.removeEventListener('mousemove', onMouseMove)
  document.removeEventListener('mouseup', onMouseUp)
}

function onMouseDown(e: MouseEvent) {
  // 记录父容器尺寸（仅第一次或每次更新均可）
  parentRect.value = parent.value!.getBoundingClientRect()

  dragging.value = true
  offsetX = e.clientX - x.value
  offsetY = e.clientY - y.value

  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)
}
</script>

<style scoped>
.page {
  position: relative;
  width: 400px;
  /* 可自行调整父容器大小 */
  height: 300px;
  border: 1px dashed #ccc;
  margin: 40px auto;
  overflow: hidden;
}

.box {
  position: absolute;
  width: 120px;
  height: 120px;
  background: #409eff;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  user-select: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
</style>
